﻿using Microsoft.SharePoint;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.UI;

namespace MetricGR.Interpolation.Components
{
    public static class Interpolation
    {

        // test interpolation string: <table><tr><td>siteTitle</td><td>{siteTitle}</td></tr><tr><td>siteUrl</td><td>{siteUrl}</td></tr><tr><td>webTitle</td><td>{webTitle}</td></tr><tr><td>webUrl</td><td>{webUrl}</td></tr><tr><td>listTitle</td><td>{listTitle}</td></tr><tr><td>listDescription</td><td>{listDescription}</td></tr><tr><td>listAuthorLoginName</td><td>{listAuthorLoginName}</td></tr><tr><td>listAuthorName</td><td>{listAuthorName}</td></tr><tr><td>listDefaultDisplayFormUrl</td><td>{listDefaultDisplayFormUrl}</td></tr><tr><td>listDefaultEditFormUrl</td><td>{listDefaultEditFormUrl}</td></tr><tr><td>listDefaultNewFormUrl</td><td>{listDefaultNewFormUrl}</td></tr><tr><td>listDefaultView</td><td>{listDefaultView}</td></tr><tr><td>listDefaultViewUrl</td><td>{listDefaultViewUrl}</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>item id</td><td>{ID}</td></tr><tr><td>item title</td><td>{title}</td></tr></table>

        public static string ToString(string InterpolationString, SPSite site, SPWeb web, SPList list, SPListItem listItem)
        {
            // string value = string.Empty;

            // Some string format PARAMS from the given objects
            Dictionary<string, string> stringTokens = new Dictionary<string, string>();
            if (site != null && site.RootWeb != null)
            {
                stringTokens.Add("siteTitle", site.RootWeb.Title);
                stringTokens.Add("siteUrl", site.RootWeb.Url);
            }

            if (web != null)
            {
                stringTokens.Add("webTitle", web.Title);
                stringTokens.Add("webUrl", web.Url);
            }

            if (list != null)
            {
                stringTokens.Add("listTitle", list.Title);
                stringTokens.Add("listDescription", list.Description);
                stringTokens.Add("listAuthorLoginName", list.Author.LoginName);
                stringTokens.Add("listAuthorName", list.Author.Name);
                stringTokens.Add("listDefaultDisplayFormUrl", list.DefaultDisplayFormUrl);
                stringTokens.Add("listDefaultEditFormUrl", list.DefaultEditFormUrl);
                stringTokens.Add("listDefaultNewFormUrl", list.DefaultNewFormUrl);
                stringTokens.Add("listDefaultView", list.DefaultView.Title);
                stringTokens.Add("listDefaultViewUrl", list.DefaultViewUrl);
            }

            stringTokens.Add("listItemID", listItem.ID.ToString());
            stringTokens.Add("listItemDisplayName", listItem.DisplayName);
            stringTokens.Add("listItemUrl", listItem.Url);


            // create a list for the fields in the list item
            Dictionary<string, Guid> fields = new Dictionary<string, Guid>(StringComparer.InvariantCultureIgnoreCase);
            listItem.Fields.OfType<SPField>().ToList().ForEach(delegate(SPField f)
            {
                if (!fields.ContainsKey(f.StaticName))
                {
                    fields[f.Title] = f.Id;
                }
            });

            if (InterpolationString == null) throw new ArgumentNullException("format");
            Regex regex = new Regex(
                @"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
                RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
            );
            List<object> values = new List<object>();
            string rewrittenFormat = regex.Replace(InterpolationString, delegate(Match m)
            {
                Group startGroup = m.Groups["start"];
                Group propertyGroup = m.Groups["property"];
                Group formatGroup = m.Groups["format"];
                Group endGroup = m.Groups["end"];

                // because we want the user to be able to ignore the case of the static field name 
                string fieldText = string.Empty;
                string fieldName = propertyGroup.Value;
                if (fields.ContainsKey(fieldName))
                {
                    // The token in the string is a list item field name, so replace it with the value of the field for this list item.
                    Guid fieldId = fields[fieldName];
                    object fieldValue = listItem[fieldId];
                    if (fieldValue != null)
                    {
                        fieldText = fieldValue.ToString();
                    }
                }
                else if (stringTokens.ContainsKey(fieldName))
                {
                    // test to see if any of the special tokens have been used in the format string.
                    fieldText = stringTokens[fieldName];
                }

                // values.Add((item[propertyGroup.Value] == null) ? string.Empty : item[propertyGroup.Value].ToString());
                values.Add(fieldText);

                return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count);
            });


            return string.Format(null, rewrittenFormat, values.ToArray());


            // ?????????????????????????????????????????????????????
            // return value;
        }

        /// <summary>
        /// Replacement method for the built-in string.Format method, instead of using {x}, where x 
        /// is a number you can use {text} and an object with the given text as a property.
        /// </summary>
        /// <example>
        ///         var student = new { name = "John" };
        ///         Common.FormatNamed("...{name}...", student);
        /// Result: "...John..."
        /// </example>
        public static string FormatNamed(string format, object source)
        {
            if (format == null) throw new ArgumentNullException("format");
            Regex regex = new Regex(
                @"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
                RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
            );
            List<object> values = new List<object>();
            string rewrittenFormat = regex.Replace(format, delegate(Match m)
            {
                Group startGroup = m.Groups["start"];
                Group propertyGroup = m.Groups["property"];
                Group formatGroup = m.Groups["format"];
                Group endGroup = m.Groups["end"];
                values.Add((propertyGroup.Value == "0") ? source : DataBinder.Eval(source, propertyGroup.Value));
                return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count);
            });
            return string.Format(null, rewrittenFormat, values.ToArray());
        }

        public static string FormatNamed(string template, SPListItem item)
        {
            // EXAMPLE USAGE: 
            //      string template = "<b>{title}</b>, <b>{CustomColumn}</b>, <b>{NonExistantColumn}</b>";
            //      string listUrl = "/Lists/SomeCustomList/AllItems.aspx";
            //      SPList list = Common.GetListByUrl(listUrl);
            //      SPListItem source = list.Items[0];
            //      string formatvalue = Common.FormatNamed(template, source);

            // create a list for the fields in the list item
            Dictionary<string, Guid> fields = new Dictionary<string, Guid>(StringComparer.InvariantCultureIgnoreCase);
            item.Fields.OfType<SPField>().ToList().ForEach(delegate(SPField f)
            {
                if (!fields.ContainsKey(f.StaticName))
                {
                    fields[f.Title] = f.Id;
                }
            });

            if (template == null) throw new ArgumentNullException("format");
            Regex regex = new Regex(
                @"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
                RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
            );
            List<object> values = new List<object>();
            string rewrittenFormat = regex.Replace(template, delegate(Match m)
            {
                Group startGroup = m.Groups["start"];
                Group propertyGroup = m.Groups["property"];
                Group formatGroup = m.Groups["format"];
                Group endGroup = m.Groups["end"];

                // because we want the user to be able to ignore the case of the static field name 
                string fieldText = string.Empty;
                string fieldName = propertyGroup.Value;
                if (fields.ContainsKey(fieldName))
                {
                    Guid fieldId = fields[fieldName];
                    object fieldValue = item[fieldId];
                    if (fieldValue != null)
                    {
                        fieldText = fieldValue.ToString();
                    }
                }

                // values.Add((item[propertyGroup.Value] == null) ? string.Empty : item[propertyGroup.Value].ToString());
                values.Add(fieldText);

                return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count);
            });
            return string.Format(null, rewrittenFormat, values.ToArray());
        }
    }
}
